package com.studyroom.config;

import com.studyroom.util.CommonResponseEnum;
import com.studyroom.util.ResponseEntity;
import com.studyroom.util.ResponseUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.expression.ExpressionException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.io.IOException;

import static com.studyroom.util.CommonResponseEnum.REQUEST_METHOD_NOT_SUPPORTED;
import static com.studyroom.util.CommonResponseEnum.VALIDATE_FAILED;


/**
 * @author lazy-turtle
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    /**
     * 处理空指针的异常
     */
    @ExceptionHandler(NullPointerException.class)
    public ResponseEntity<?> nullPointerHandler(HttpServletRequest req, NullPointerException e) {
        printErrorInterfaceInfo();
        log.info("空指针异常,原因是-------->", e);
        return ResponseUtils.fail("空指针");
    }

    /**
     * 参数异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<?> methodArgumentException(MethodArgumentNotValidException e) {
        // 拿ObjectError错误信息 只拿一条
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        // 返回错误提示信息
        return ResponseUtils.fail(VALIDATE_FAILED, "参数错误:" + objectError.getDefaultMessage());

    }

    /**
     * 参数转换错误
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity<?> httpMessageNotReadableException() {
        return ResponseUtils.fail(VALIDATE_FAILED, "请求参数的数据类型有误");
    }

    /**
     * 参数异常
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<?> directMethodArgumentException(ConstraintViolationException e) {
        // 拿ObjectError错误信息 只拿一条
        ConstraintViolation<?> error = e.getConstraintViolations().iterator().next();
        // 返回错误提示信息
        return ResponseUtils.fail(VALIDATE_FAILED, "参数错误:" + error.getMessage());

    }

    /**
     * 校验参数异常
     */
    @ExceptionHandler(BindException.class)
    public ResponseEntity<?> validBindExceptionHandle(BindException e) {
        // 拿ObjectError错误信息 只拿一条
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        // 返回错误提示信息
        return ResponseUtils.fail(VALIDATE_FAILED, "参数错误:" + objectError.getDefaultMessage());
    }


    /**
     * 405
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseEntity<?> httpRequestMethodNotSupportedException(HttpServletRequest req, Exception e) {
        log.info("405,原因是-------->" + "请求方法错误");
        return ResponseUtils.fail(REQUEST_METHOD_NOT_SUPPORTED,null);
    }

    /**
     * 处理其他异常
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<?> exceptionHandler(HttpServletRequest req, Exception e) {
        printErrorInterfaceInfo();
        log.info("未知异常,原因是-------->", e);
        return ResponseUtils.fail();
    }

    /**
     * 处理其他异常
     */
    @ExceptionHandler(ExpressionException.class)
    public ResponseEntity<?> exceptionHandlerRun(HttpServletRequest req, Exception e) {
        printErrorInterfaceInfo();
        log.info("未知异常,原因是-------->", e);
        return ResponseUtils.fail("当前未登录, 请先登录");
    }

    /**
     * IO异常
     */
    @ExceptionHandler(IOException.class)
    public ResponseEntity<?> ioExceptionHandler(HttpServletRequest req, IOException e) {
        printErrorInterfaceInfo();
        log.info("IO异常,原因是-------->", e);
        return ResponseUtils.fail();
    }

    private void printErrorInterfaceInfo() {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert servletRequestAttributes != null;
        HttpServletRequest request = servletRequestAttributes.getRequest();
        StringBuffer requestUrl = request.getRequestURL();
        String method = request.getMethod();
        log.info("URL : " + requestUrl + "  Method :" + method);
    }

}
